﻿using System;
using repliback4.Net.ServerSide;
using repliback4.Net.Identification;
using Tools.Debugging;
using repliback4.Net.Protocol.SASLMechanisms;
using System.Security.Authentication;
using repliback4.Xml.Extensions;


namespace repliback4.Net.Protocol.States.ServerSide {
    public abstract class ServerState : State {

        protected ServerStateManager Manager {
            get { return base.manager as ServerStateManager; }
        }

        protected XmlStreaming.XmlStreamWriter Writer {
            get { return this.Manager.Client.writer; }
        }
        protected ServerState ( StateManager manager )
            : base( manager ) {

            Logger.Debug( this, "in state" );
        }
    }


    public class DisconnecedState : ServerState {
        public DisconnecedState ( StateManager manager )
            : base( manager ) {

        }




        public override void ExecuteXmlDeclaration ( System.Xml.XmlNode xmlDeclaration ) {
            Logger.Debug( this, "recived xmldeclaration, changing to 'Connecting State'" );
            this.manager.CurrentState = new ConnectingState( this.Manager );
        }

    }

    public class ConnectingState : ServerState {

        public ConnectingState ( StateManager manager ) : base( manager ) { }





        public override void ExecuteStreamStart ( repliback4.Net.Protocol.Messages.BaseStream stream ) {
            Logger.Debug( this, "ExecuteStreamStart" );
            //throw new NotImplementedException( );
            this.Writer.OpenDocument( );

            repliback4.Net.Protocol.Messages.Stream sStream = new repliback4.Net.Protocol.Messages.Stream( this.Writer.Document );
            sStream.From = "server.net";

            this.Writer.OpenStream( sStream );

            if ( !this.Manager.Client.ClientInfo.Encrypted && !this.Manager.Client.ClientInfo.Authenticated ) {
                repliback4.Net.Protocol.Messages.FeaturesStream fStream = new repliback4.Net.Protocol.Messages.FeaturesStream( this.Writer.Document );
                this.Writer.OpenStream( fStream );

                repliback4.Net.Protocol.Messages.StartTLS starttls = new repliback4.Net.Protocol.Messages.StartTLS( this.Writer.Document );
                starttls.Required = true;
                this.Writer.WriteStanza( starttls );
                this.Writer.CloseStream( );

            } else {

                if ( !this.Manager.Client.ClientInfo.Authenticated ) {
                    repliback4.Net.Protocol.Messages.FeaturesStream fStream = new repliback4.Net.Protocol.Messages.FeaturesStream( this.Writer.Document );
                    this.Writer.OpenStream( fStream );
                    repliback4.Net.Protocol.Messages.SASLMechanisms mechanisms = new repliback4.Net.Protocol.Messages.SASLMechanisms( this.Writer.Document );
                    foreach ( MechanismsRegistryEntry entry in this.Manager.Client.Server.authMechanism.GetEnabledMechanisms( ) ) {
                        mechanisms.AddMechanism( entry.Name );
                    }
                    this.Writer.WriteStanza( mechanisms );
                    this.Writer.CloseStream( );

                } else {
                    repliback4.Net.Protocol.Messages.FeaturesStream fStream = new repliback4.Net.Protocol.Messages.FeaturesStream( this.Writer.Document );
                    this.Writer.OpenStream( fStream );
                    //Logger.DebugFormat( this, "zaczynamy normalna komunikacje" );

                    Protocol.Messages.Bind bind = new repliback4.Net.Protocol.Messages.Bind( this.Writer.Document );
                    Protocol.Messages.Session sess = new repliback4.Net.Protocol.Messages.Session( this.Writer.Document );

                    this.Writer.WriteStanza( bind );
                    this.Writer.WriteStanza( sess );

                    this.Writer.CloseStream( );

                    this.Manager.CurrentState = new ConnectedState( this.Manager );

                }
            }
            //repliback4.Net.Protocol.Messages.FeaturesStream fStream = new repliback4.Net.Protocol.Messages.FeaturesStream( this.Writer.Document );
            //this.Writer.OpenStream( fStream );
            //this.Writer.CloseStream( );

            this.Writer.Flush( );
        }

        public override void ExecuteTag ( repliback4.Net.Protocol.Messages.ReplibackTag stanza ) {
            Type t = stanza.GetType( );
            if ( t == typeof( repliback4.Net.Protocol.Messages.SASLAuth ) ) {
                this.manager.CurrentState = new SASLAuthorizingState( this.Manager );
                this.manager.CurrentState.ExecuteTag( stanza );
            } else if ( t == typeof( repliback4.Net.Protocol.Messages.StartTLS ) ) {
                repliback4.Net.Protocol.Messages.ProceedTLS proceed = new repliback4.Net.Protocol.Messages.ProceedTLS( this.Writer.Document );
                this.Writer.WriteStanza( proceed );
                this.Writer.Flush( );

                if ( this.Manager.Client.serverConection.TrySwitchToSecure( ) ) {
                    this.Manager.Client.ClientInfo.Encrypted = true;
                    this.Manager.CurrentState = new DisconnecedState( this.Manager );
                } else {

                }


            } else {
                throw new UnknowStepStateException( "unknown step", this, stanza );
            }
        }

    }

    public class SASLAuthorizingState : ServerState {
        public SASLAuthorizingState ( StateManager manager ) : base( manager ) { }


        public override void ExecuteTag ( repliback4.Net.Protocol.Messages.ReplibackTag stanza ) {
            Type t = stanza.GetType( );
            if ( t == typeof( repliback4.Net.Protocol.Messages.SASLAuth ) ) {
                repliback4.Net.Protocol.Messages.SASLAuth _stanza = stanza as repliback4.Net.Protocol.Messages.SASLAuth;
                State state = this.Manager.Client.Server.authMechanism.LookupByName( _stanza.Type ).GetHandlingState( this.Manager );
                state.ExecuteTag( stanza );
            } else {
                throw new UnknowStepStateException( "unknown step", this, stanza );
            }
        }
    }

    public class PLAIN_SASLAuthorizingState : ServerState {
        public PLAIN_SASLAuthorizingState ( StateManager manager )
            : base( manager ) {

        }

        public override void ExecuteTag ( repliback4.Net.Protocol.Messages.ReplibackTag stanza ) {
            // base.ExecuteTag( stanza );

            SASLMechanisms.Plain codec = new Plain( );
            codec.MessageB64 = ( ( repliback4.Net.Protocol.Messages.SASLAuth ) stanza ).Message;
            Logger.Debug( this, "acid: {0}, azid: {1}, pass: {2}", codec.AuthenticationIdentity, codec.AuthorizationIdentity, codec.Password );

            //autentyfikacja w serverze....
            // ....
            //success or failed
            if (/*authorised*/true ) {
                this.Manager.Client.ClientInfo.Authenticated = true;
                this.Manager.Client.ClientInfo.Rid = new Rid( codec.AuthorizationIdentity , this.Manager.Client.Server.Settings.HostName );
                repliback4.Net.Protocol.Messages.SASLAuthSuccess success = new repliback4.Net.Protocol.Messages.SASLAuthSuccess( this.Writer.Document );
                this.Writer.WriteStanza( success );
                this.Writer.Flush( );
            } else {
                repliback4.Net.Protocol.Messages.SASLAuthFailure failed = new repliback4.Net.Protocol.Messages.SASLAuthFailure( this.Writer.Document );
                failed.Condition = FailureConditions.NotAuthorized;

                this.Writer.WriteStanza( failed );
                this.Writer.Flush( );
                this.Writer.CloseStream( );
            }
            this.Manager.CurrentState = new DisconnecedState( this.Manager );
        }
    }

    public class DIGESTMD5_SASLAuthorizingState : ServerState {
        public DIGESTMD5_SASLAuthorizingState ( StateManager manager )
            : base( manager ) {

        }
    }

    public class ConnectedState : ServerState {
        public ConnectedState ( StateManager manager )
            : base( manager ) {

        }

        public override void ExecuteTag ( repliback4.Net.Protocol.Messages.ReplibackTag stanza ) {
            
            switch ( stanza.TagName ) {
                case "jabber:client:iq":
                    Protocol.Messages.InfoQuery iq = stanza as Protocol.Messages.InfoQuery;
                    if ( iq.HasQuery ) {
                        Protocol.Messages.Stanza query = iq.Query;
                        Logger.Debug( this, "ExecuteTag :: iq:{0}, query: {1} [{2}]", iq.Type, query.Xml.Name, query.GetType( ).FullName );
                    } else {
                        Logger.Debug( this, "ExecuteTag :: iq:{0}, no query" );
                    }
                    this.Manager.Client.Queue.Enque( QueueDirection.In, iq );
                    break;
                case "jabber:client:message":
                    Logger.Debug( this, "ExecuteTag :: message" );
                    break;
                case "jabber:client:presence":
                    Logger.Debug( this, "ExecuteTag :: presence" );
                    break;

                default:
                    break;

            }
        }

    }


}
